//用法new Complie(el,vm)

class Compile {
    constructor(el, vm) {
        //遍历宿主节点
        this.$el = document.querySelector(el);
        this.$vm = vm;
        //编译
        if (this.$el) {
            //转换内部内容为片段Fragment
            this.$fragment = this.node2Fragment(this.$el);
            //执行编译
            this.compile(this.$fragment);
            //将编译完的html结果追加至$el
            this.$el.appendChild(this.$fragment)
        }
    }

    //将宿主元素中代码片段拿出来遍历，这样做比较高效
    node2Fragment(el) {
        //el是整个虚拟dom
        //创建一个虚拟dom节点对象
        const frag = document.createDocumentFragment();
        //将el中所有子元素搬家至frag中
        let child;
        while (child = el.firstChild) {
            frag.appendChild(child)
        }
        return frag;
    }
    //编译过程
    compile(el) {
        //el是正虚拟dom
        const childNodes = el.childNodes;
        //遍历
        Array.from(childNodes).forEach(node => {
            //判断类型
            if (this.isElement(node)) {
                //元素
                //console.log(`编译元素${node.nodeName}`)
                //查找m-  @ :
                const nodeAttrs = node.attributes;
                Array.from(nodeAttrs).forEach(attr => {
                    const attrName = attr.name;//属性名
                    const exp = attr.value;//属性值
                    if (this.isDirective(attrName)) {
                        //m-text是指令
                        const dir = attrName.substring(2);
                        //执行指令
                        this[dir] && this[dir](node, this.$vm, exp)
                    }
                    if (this.isEvent(attrName)) {
                        const dir = attrName.substring(1);//@click
                        this.eventHandler(node, this.$vm, exp, dir)
                    }
                })
            } else if (this.isText(node)) {
                //文本
                //console.log(`编译文本${node.textContent}`)
                this.compileText(node)
            }
            //递归子节点
            if (node.childNodes && node.childNodes.length !== 0) {
                this.compile(node)
            }
        })
    }
    compileText(node) {
        this.update(node, this.$vm, RegExp.$1, 'text')
    }
    //更新函数
    update(node, vm, exp, dir) {
        const updaterFn = this[dir + 'Updater'];
        //初始化
        updaterFn && updaterFn(node, vm[exp]);
        //收集依赖
        new Watcher(vm, exp, function (value) {
            updaterFn && updaterFn(node, value)
        })
    }
    //事件处理器
    eventHandler(node, vm, exp, dir) {
        let fn = vm.$options.methods && vm.$options.methods[exp];
        if (dir && fn) {
            node.addEventListener(dir, fn.bind(vm))
        }
    }
    text(node, vm, exp) {
        this.update(node, vm, exp, 'text')
    }
    //双向绑定
    model(node, vm, exp) {
        //指定input的value属性
        this.update(node, vm, exp, 'model');
        //视图对模型响应上
        node.addEventListener('input', e => {
            vm[exp] = e.target.value
        })
    }
    html(node, vm, exp) {
        this.update(node, vm, exp, 'html')
    }
    htmlUpdater(node, value) {
        node.innerHTML = value
    }
    textUpdater(node, value) {
        console.log(node)
        node.textContent = value
    }
    //是否是指令
    isDirective(attr) {
        return attr.indexOf('m-') == 0
    }
    isEvent(attr) {
        return attr.indexOf('@') == 0
    }

    isElement(node) {
        return node.nodeType === 1
    }
    isText(node) {
        //文本包含换行和普通的文本，所以用正则找出{{}}中间的
        return node.nodeType === 3 && /\{\{(.*)\}\}/.test(node.textContent);
    }
}